IS_CONTROLLER = True

if IS_CONTROLLER: from chimera.controllers import database
else: import database
from chimera.core.event import event
from chimera.util.position import Position
from chimera.util.sextractor import SExtractor
from chimera.util.coord import Coord
from datetime import datetime
from optparse import OptionParser
import ephem
import math
import pyfits
from types import StringType
from chimera.core.chimeraobject import ChimeraObject
from chimera.core.remoteobject import RemoteObject
import copy


class Star:
    def __init__(self, ra = None, dec = None, objname = None):
        self._objname = objname
        self._ra = ra
        self._dec = dec

    def getObjName(self):
        return self._objname

    def setObjName(self, objname):
        self._objname = objname

    def getRA(self):
        return self._ra

    def getDec(self):
        return self._dec

    def display(self):
        if self.getObjName() != None:
            print self.getObjName()
        print "RA:  ", self.getRA()
        print "Dec: ", self.getDec()


class Exposure(RemoteObject):
    #The Exposure class contains information about an entire image.
    def __init__(self, filename = None):
	RemoteObject.__init__(self)
        self._expstars = []
	# Read all the needed information from the FITS header.  	
	if filename != None:
            self._filename = filename
            hdulist = pyfits.open(filename)
	    hdulist.verify('fix')
            self._latitude = Coord.fromDMS(hdulist[0].header['LATITUDE'])
            self._longitude = Coord.fromDMS(hdulist[0].header['LONGITUD'])	    
	    obsdate = hdulist[0].header['DATE-OBS']
            obsdate = datetime.strptime(obsdate, '%Y-%m-%dT%H:%M:%S')
	    obs = ephem.Observer()
            obs.lat = self._latitude.strfcoord('%(d)d:%(m)d:%(s).2f')
	    obs.long = self._longitude.strfcoord('%(d)d:%(m)d:%(s).2f')
	    obs.pressure = 0
            obs.date = obsdate
	    #If sideareal time is in the fits header, use it.  Otherwise, calculate ST using the sidereal_time method in the python package ephem.
	    try:
		self._lst = Coord.fromHMS(hdulist[0].header['ST']).R
	    except:
		self._lst = Coord.fromHMS(str(obs.sidereal_time()))
	    #All Chimera images have the keyword FILTER in the header.  If you are using images from another observatory, the keyword might be FILTERS	
	    try:
	        self._filter = hdulist[0].header['FILTER']
	    except:
		self._filter = hdulist[0].header['FILTERS']

    def getFileName(self):
        return self._filename
    
    def getRA(self):
        return self._ra

    def getDec(self):
        return self._dec

    def getAlt(self):
        return self._alt

    def getAz(self):
        return self._az

    def getLatitude(self):
        return self._latitude

    def getLST(self):
        return self._lst

    def getFilter(self):
        return self._filter

    def appendStar(self, expstar):
        if isinstance(expstar, ExposureStar):
            self._expstars.append(expstar)

    def getAllStars(self):
        return copy.deepcopy(self._expstars)

    def getBrightestStars(self, n = 10):
        added = False
        brightestStars = []
        for expstar in self._expstars:
            i = 0
            added = False
            for stored in brightestStars:
                if expstar.getMagnitude() < stored.getMagnitude():
                    brightestStars.insert(i, expstar)
                    if len(brightestStars) > n:
                        brightestStars.pop()
                        added = True
                    break
                i = i + 1
            if len(brightestStars) < n and not added:
                brightestStars.append(expstar)
        return copy.deepcopy(brightestStars)

    def trimToBrightestStars(self, n = 10):
        self._expstars = self.getBrightestStars(n)

    def display(self):        
	print "File:", self.getFileName()

	
class ExposureStar:
    # The exposure star class contains information about individual stars in an image.  
    def __init__(self, exposure = None, star = None):
	#convert RA/Dec given by SExtractor into Alt/Az.          
	if exposure != None and star != None:
            self._star = star
            raDecTuple = star.getRA(), star.getDec()
            raDec = Position(raDecTuple)
            altAz = Position.raDecToAltAz(raDec, exposure.getLatitude(), exposure.getLST())
            self._alt = altAz.alt
            self._az = altAz.az

    def getObjName(self):
        return self._star.getObjName()

    def setObjName(self, objname):
        self._star.setObjName(objname)

    def getRA(self):
        return self._star.getRA()

    def getDec(self):
        return self._star.getDec()

    def getAlt(self):
        return self._alt

    def getAz(self):
        return self._az

    def getFlux(self):
        return self._flux

    def setFlux(self, flux):
        self._flux = flux

    def getFWHM(self):
        return self._fwhm

    def setFWHM(self, fwhm):
        self._fwhm = fwhm

    def getMagnitude(self):
        return self._mag

    def setMagnitude(self, mag):
        self._mag = mag

    def getAirmass(self):
	# Calculation of Airmass based on SExtractor outputs        
	# Zenith angle = PI/2 - Altitude
        # Airmass = sec(Zenith angle)
        return 1 / math.cos(math.pi / 2 - self._alt.R)


    def display(self):      
	# Print Photometry results to the terminal.  This is used only if Photometry is being run as a util.	
	if self.getObjName() != None:
            print self.getObjName()
        print "RA:  ", self.getRA()
        print "Dec: ", self.getDec()
        print "Alt: ", self.getAlt()
	print "Az: ", self.getAz()
        print "Mag: ", self.getMagnitude()
        print "Flux:", self.getFlux()
        print "FWHM:", self.getFWHM()
        print "AM:  ", self.getAirmass()
       
	
class Photometry(ChimeraObject):
    exposure = None
    expstars = []

    def _getStar(self, ra, dec):
        return Star(ra, dec)

    def run(self, exposure, stars = None, n=30):
	'''Use SExtractor to find the RA/DEC, flux, FWHM, Alt/Az, and magnitude of stars and write the results to the database.  If the list stars is empty, the brightest n (default 30) stars will be written to the database.  To write specific stars to the database, create a file including the ra, dec, and object name of each star.'''
        if exposure != None:
            if isinstance(exposure, StringType):
                exposure = Exposure(exposure)
            if isinstance(exposure, Exposure):
                self.exposure = exposure
		
                # Extract point sources and gather data
                sex = SExtractor()
                sex.config['MEMORY_PIXSTACK'] = 9900000 # default 300000
	    	sex.config['BACK_TYPE'] = "AUTO"
	    	sex.config['DETECT_THRESH'] = 3.0
            	sex.config['DETECT_MINAREA'] = 18.0           
	        #sex.config['PHOT_AUTOPARAMS'] = 10,10
                sex.config['PARAMETERS_LIST'] = ['NUMBER',
                                                 'X_IMAGE',     # Pixels
                                                 'Y_IMAGE',     # Pixels
                                                 'X_WORLD',     # D (convert to Coord)
                                                 'Y_WORLD',     # D (convert to Coord)
                                                 'MAG_BEST',    # Magnitude
                                                 'FWHM_IMAGE',  # Pixels (or should it be'FWHM_WORLD'?)
                                                 'FLUX_BEST']   # Count
                sex.run(exposure.getFileName())
                catalog = sex.catalog()

                # Store results
                for item in catalog:
                    # Convert pixel coordinates of every point source to RA/DEC
		    star = self._getStar(Coord.fromD(item['X_WORLD']).toHMS(),
                                         Coord.fromD(item['Y_WORLD']).toDMS())
		    #get photometry info on all stars, if a list of stars was not given	
		    if (stars == None):
			expstar = ExposureStar(exposure, star)
                    	expstar.setFWHM(item['FWHM_IMAGE'])
                    	expstar.setFlux(item['FLUX_BEST'])
                    	expstar.setMagnitude(item['MAG_BEST'])
                    	expstar.setObjName('Star ' + str(item['NUMBER']))
		    	exposure.appendStar(expstar)	
		    else:
			for s in stars:
				# Get photometry on stars with an ra and dec within 2 arcseconds of objects in the list stars		                
				TOLERANCE1 = 0.008
				TOLERANCE2 = 0.0006
				if(abs(Coord.fromHMS(s[0]).D-star.getRA().D)<TOLERANCE1) and (abs(Coord.fromDMS(s[1]).D-star.getDec().D)<TOLERANCE2):
					expstar = ExposureStar(exposure, star)
                    			expstar.setFWHM(item['FWHM_IMAGE'])
                    			expstar.setFlux(item['FLUX_BEST'])
                    			expstar.setMagnitude(item['MAG_BEST'])
                    			expstar.setObjName(s[2])
		    			exposure.appendStar(expstar)

		#Trigger photometryCompleted event
		sex.clean(config=True, catalog=False, check=True)

		#Only select the brightest stars if no stars are specified in a list. Default number of stars is 30, but n can be selected when running the script chimera-photo. 
		if stars == None:
			exposure.trimToBrightestStars(n)                	

		daemon = self.getDaemon()
		if daemon == None:		
			self.photometryCompleted(exposure)
			return exposure
		else:
			daemon.connect(exposure)
			self.photometryCompleted(exposure.getProxy())
			return exposure.getProxy()

                

                
    @event
    def photometryCompleted(self, exposure):
        pass


